package com.xms.sdk.client.aggroot;

import com.xms.core.data.Entity;
import com.xms.core.enums.OperationStage;
import com.xms.core.enums.OperationType;
import com.xms.core.exception.XmsException;
import com.xms.plugin.service.IFormPluginExecutor;
import com.xms.schema.entity.Attribute;
import com.xms.schema.entity.RelationShip;
import com.xms.schema.service.attribute.IAttributeFinderService;
import com.xms.schema.service.relationship.IRelationShipFinderService;
import com.xms.sdk.client.DataProviderBase;
import com.xms.sdk.core.AggregateRootMetaData;
import com.xms.sdk.core.EntityAttributeMetadata;
import com.xms.sdk.core.EntityReference;
import com.xms.sdk.data.IOrganizationDataProvider;
import com.xms.sdk.service.IDataCreater;
import com.xms.sdk.service.IDataDeleter;
import com.xms.sdk.service.IDataUpdater;
import com.xms.utils.CollectionUtil;
import com.xms.utils.UUIDUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * <p>
 * AggUpdater
 * </p>
 *
 * @author migo
 * @since 2021/9/8
 */
@Service
public class AggUpdater extends DataProviderBase implements IAggUpdater{
    private IOrganizationDataProvider _organizationDataProvider;
    private IDataCreater _dataCreater;
    private IDataUpdater _dataUpdater;
    private IDataDeleter _dataDeleter;
    private IRelationShipFinderService _relationShipFinderService;
    private IAttributeFinderService _attributeFinderService;
    private IFormPluginExecutor<AggregateRoot, AggregateRootMetaData> _formPluginExecutor;
    @Autowired
    public void AggUpdater(IOrganizationDataProvider organizationDataProvider,
                           IDataCreater dataCreater,
                           IDataUpdater dataUpdater,
                           IDataDeleter dataDeleter,
                           IRelationShipFinderService relationShipFinderService,
                           IAttributeFinderService attributeFinderService,
                           IFormPluginExecutor<AggregateRoot, AggregateRootMetaData> formPluginExecutor) {
        _organizationDataProvider = organizationDataProvider;
        _dataCreater = dataCreater;
        _dataUpdater = dataUpdater;
        _dataDeleter = dataDeleter;
        _relationShipFinderService = relationShipFinderService;
        _attributeFinderService = attributeFinderService;
        _formPluginExecutor = formPluginExecutor;
    }

    private AggregateRootMetaData getAggregateRootMetaData(AggregateRoot aggregateRoot, UUID systemFormId) throws XmsException {
        AggregateRootMetaData aggRootMetaData = new AggregateRootMetaData();
        com.xms.schema.entity.Entity entityMetadata = getEntityMetaData(aggregateRoot.MainEntity.Name);
        List<Attribute> attributeMetadatas = _attributeFinderService.getByEntityId(entityMetadata.getEntityid());
        EntityAttributeMetadata eam = new EntityAttributeMetadata();
        eam.EntityMetadata = entityMetadata;
        eam.AttributeMetadatas = attributeMetadatas;
        aggRootMetaData.MainMetadata = eam;
        aggRootMetaData.SystemFormId = systemFormId;
        aggRootMetaData.ListMetadatas = new HashMap<>();
        for (RefEntity refEntity : aggregateRoot.ChildEntities)
        {
            entityMetadata = getEntityMetaData(refEntity.Name);
            attributeMetadatas = _attributeFinderService.getByEntityId(entityMetadata.getEntityid());
            eam = new EntityAttributeMetadata();
            eam.EntityMetadata = entityMetadata;
            eam.AttributeMetadatas = attributeMetadatas;
            if (aggRootMetaData.ListMetadatas.containsKey(refEntity.Name))
            {
                aggRootMetaData.ListMetadatas.put(refEntity.Name, eam);
            }
        }
        return aggRootMetaData;
    }

    @Override
    public boolean update(AggregateRoot aggregateRoot, UUID systemFormId, boolean ignorePermissions) throws XmsException {
        AggregateRootMetaData aggRootMetaData = getAggregateRootMetaData(aggregateRoot, systemFormId);
        boolean result = false;
        AggregateRoot originalAggroot = new AggregateRoot();
        try
        {
            internalOnUpdate(originalAggroot, aggregateRoot, OperationStage.PreOperation, aggRootMetaData);
            UUID thisId = aggregateRoot.MainEntity.getIdValue();
            for (RefEntity c : aggregateRoot.ChildEntities)
            {
                String name = c.Name, relationshipname = c.Relationshipname, refname = "";
                if (!relationshipname.isEmpty())
                {
                    RelationShip relationShipMetas = _relationShipFinderService.getByName(c.Relationshipname);
                    if (null != relationShipMetas && relationShipMetas.getReferencedentityid().equals(c.Entityid))
                    {
                        refname = relationShipMetas.getReferencingattributename();
                    }
                    if (!refname.isEmpty())
                    {
                        c.Entity.setValue(refname, new EntityReference(aggregateRoot.MainEntity.Name, thisId));
                    }
                }
            }
            List<Entity> childEntities = aggregateRoot.ChildEntities.stream().filter(x -> x.Entitystatus != OperationType.Delete.value()).map(x->x.Entity).collect(Collectors.toList());
            List<String> entityNames = childEntities.stream().map(n -> n.Name).distinct().collect(Collectors.toList());
            for (String item : entityNames)
            {
                List<Entity> items = childEntities.stream().filter(n -> n.Name.equalsIgnoreCase(item)).collect(Collectors.toList());
                List<Entity> creatingRecords = items.stream().filter(n -> n.Name.equalsIgnoreCase(item) && UUIDUtil.isEmpty(n.getIdValue())).collect(Collectors.toList());
                if (!creatingRecords.isEmpty())
                {
                    _dataCreater.createMany(creatingRecords);
                }
                List<Entity> updatingEntities = items.stream().filter(n -> n.Name.equalsIgnoreCase(item) && UUIDUtil.notEmpty(n.getIdValue())).collect(Collectors.toList());
                if(CollectionUtil.notEmpty(updatingEntities)) {
                    for (Entity updItem : updatingEntities) {
                        _dataUpdater.update(updItem);
                    }
                }
            }
            List<Entity> childEntitiesDelete = aggregateRoot.ChildEntities.stream().filter(x -> x.Entitystatus == OperationType.Delete.value()).map(x -> x.Entity).collect(Collectors.toList());
            List<String> entityNamesDelete = childEntitiesDelete.stream().map(n -> n.Name).distinct().collect(Collectors.toList());
            for (String item : entityNamesDelete)
            {
                List<Entity> items = childEntitiesDelete.stream().filter(n -> n.Name.equalsIgnoreCase(item)).collect(Collectors.toList());
                for (Entity deleteItem : items)
                {
                    _dataDeleter.delete(deleteItem);
                }
            }

            result = _dataUpdater.update(aggregateRoot.MainEntity);
            internalOnUpdate(originalAggroot, aggregateRoot, OperationStage.PostOperation, aggRootMetaData);
        }
        catch (Exception e)
        {
            onException(e);
        }
        return result;
    }

    @Override
    public boolean update(AggregateRoot aggregateRoot, UUID systemFormId) throws XmsException {
        return this.update(aggregateRoot, systemFormId, false);
    }

    @Override
    public boolean update(AggregateRoot aggregateRoot) throws XmsException {
        return this.update(aggregateRoot, null);
    }

    private void internalOnUpdate(AggregateRoot originData, AggregateRoot newData, OperationStage stage, AggregateRootMetaData aggRootMetaDatas)
    {
        //plugin
        UUID entityId = aggRootMetaDatas.MainMetadata.EntityMetadata.getEntityid();
        _formPluginExecutor.execute(entityId, aggRootMetaDatas.SystemFormId, OperationType.Update, stage, newData, aggRootMetaDatas);
        if (stage == OperationStage.PreOperation)
        {
            //_eventPublisher.Publish(new AggRootUpdatingEvent(originData, newData) { AggRootMetaData = aggRootMetaDatas });
        }
        else if (stage == OperationStage.PostOperation)
        {
            //_eventPublisher.Publish(new AggRootUpdatedEvent(originData, newData) { AggRootMetaData = aggRootMetaDatas });
        }
    }
}
